package com.lintesis.utils.reflection;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Vector;

/**
 * 
 * @author matias.jarab
 * 
 */
@SuppressWarnings("unchecked")
public class ReflectionUtils
{

	/**
	 * 
	 * @param object1
	 *            The first object to compare
	 * @param object2
	 *            The second object to compare
	 * @param depthLevel
	 *            Indicates how depth will the method compare the collections
	 *            associated with the object if its = 0, the method will NOT
	 *            compare de inners collections
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */

	public static boolean compareObjects(Object object1, Object object2, int depthLevel) throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException
	{
		for (Field field : ReflectionUtils.getAllFields(object1))
		{
			Method getter = ReflectionUtils.findGetter(object1, field);
			if (getter == null)
				continue;
			Object value1;
			Object value2;

			if (object1.getClass().equals(object2.getClass()))
			{
				value1 = getter.invoke(object1, null);
				value2 = getter.invoke(object2, null);
				if ((value1 == null && value2 != null) || (value1 != null && value2 == null))
					return false;
				if (value1 instanceof GregorianCalendar && ((GregorianCalendar) value1).getTime() == ((GregorianCalendar) value2).getTime())
					continue;
				if (!(value1 instanceof Collection) && value1 != null && !value1.equals(value2))
					return false;
				if (value1 instanceof Collection && !ReflectionUtils.compareCollections((Collection) value1, (Collection) value2, depthLevel))
					return false;
			}
		}
		return true;
	}

	public static boolean compareCollections(Collection collection1, Collection collection2, int depthLevel) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException
	{
		if (depthLevel < 0)
			return true;
		for (Object object1 : collection1)
		{
			boolean exists = false;
			for (Object object2 : collection2)
				if (ReflectionUtils.compareObjects(object1, object2, depthLevel - 1))
				{
					exists = true;
				}
			if (!exists)
				return false;
		}
		for (Object object2 : collection2)
		{
			boolean exists = false;
			for (Object object1 : collection1)
				if (ReflectionUtils.compareObjects(object1, object2, depthLevel - 1))
				{
					exists = true;
				}
			if (!exists)
				return false;
		}
		return true;
	}

	public static Method findGetter(Object object, Field field)

	{
		String getter = field.getName().substring(0, 1).toUpperCase();
		getter += field.getName().substring(1);
		if (field.getType().equals(Boolean.class))
		{
			getter = "is" + getter;
		} else
		{
			getter = "get" + getter;
		}
		Method retorno = null;
		try
		{
			retorno = object.getClass().getMethod(getter, (null));
		} catch (SecurityException e)
		{
		} catch (NoSuchMethodException e)
		{
		}
		return retorno;
	}

	public static Method findSetter(Object object, Field field)

	{
		String setter = field.getName().substring(0, 1).toUpperCase();
		setter += field.getName().substring(1);
		setter = "set" + setter;
		Method retorno = null;
		try
		{
			retorno = object.getClass().getMethod(setter, field.getType());
		} catch (SecurityException e)
		{
		} catch (NoSuchMethodException e)
		{
		}
		return retorno;
	}

	public static Vector<Field> getAllFields(Object object)
	{

		Class<? extends Object> clase = object.getClass();
		Vector<Field> atributos = new Vector<Field>();
		if (object == null)
			return atributos;
		if (clase.getSuperclass() != null && clase.getSuperclass() != Object.class)
			try
			{
				atributos = getAllFields(clase.getSuperclass().newInstance());
			} catch (Exception e)
			{
				// // TODO Auto-generated catch block
				// e.printStackTrace();
			}
		atributos.addAll(Arrays.asList(clase.getDeclaredFields()));
		return atributos;
	}

	@SuppressWarnings("unchecked")
	public static Object clone(Object o)
	{
		Object clone = null;
		if (o == null)
			return clone;
		try
		{
			clone = o.getClass().newInstance();
		} catch (InstantiationException e)
		{
			// TODO Auto-generated catch block
			return o;
			// e.printStackTrace();
		} catch (IllegalAccessException e)
		{
			// TODO Auto-generated catch block
			// e.printStackTrace();
		}

		// Walk up the superclass hierarchy
		if (o instanceof Collection)
		{
			ArrayList set = new ArrayList();
			for (Object o2 : (Collection) o)
			{
				set.add(clone(o2));
			}
			return set;
		}
		if(o instanceof GregorianCalendar)
		{
			((GregorianCalendar)clone).setTimeInMillis(((GregorianCalendar) o).getTimeInMillis());
			return clone;
		}
		Vector<Field> fields = getAllFields(o);
		for (Field field : fields)
		{
			field.setAccessible(true);
			try
			{
				field.set(clone, field.get(o));
			} catch (IllegalArgumentException e)
			{
				// e.printStackTrace();
			} catch (IllegalAccessException e)
			{
				// e.printStackTrace();
			}
		}


		return clone;
	}

	public static HashMap<String, Object> toHashMap(Object bo)
	{
		HashMap<String, Object> retorno = new HashMap<String, Object>();

		for (Field field : ReflectionUtils.getAllFields(bo))
		{
			Object value = null;

			Method getter = findGetter(bo, field);
			if (getter != null)
			{
				try
				{
					value = getter.invoke(bo, null);
				} catch (Exception e)
				{
					// e.printStackTrace();
				}
				// if (value != null)
				retorno.put(field.getName(), clone(value));

			}
		}

		return retorno;
	}

	@SuppressWarnings("unchecked")
	public static void fromHashMap(HashMap<String, Object> hashMap, Object object)
	{
		for (Field field : getAllFields(object))
		{
			if (hashMap.get(field.getName()) != null)
			{
				try
				{
					Method setter = findSetter(object, field);
					setter.invoke(object, hashMap.get(field.getName()));
				} catch (Exception e)
				{
					// TODO Auto-generated catch block
					// e.printStackTrace();
				}
			}
		}
	}
}
